﻿using System.Collections.Generic;
using System.Linq;

namespace VirtualRink.Library
{
	public class Statistics
	{
		IRepository repository;
		List<Player> playersAffected;
		List<Team> teamsAffected;

		public Statistics() : this(new LinqRepository()) { }

		public Statistics(IRepository repository)
		{
			this.teamsAffected = new List<Team>();
			this.playersAffected = new List<Player>();
			this.repository = repository;
		}

		public void EnsureStandingsExist(Season season)
		{
			League league = season.League;
			IList<Team> teams = this.repository.GetTeamsByLeague(league.Id);

			foreach (Team team in teams)
			{
				var standings = from s in team.Standings
								where s.SeasonId == season.Id
								select s;
				if (standings.Count() == 0)
				{
					Standing standing = new Standing()
					{
						Season = season,
						League = league,
						Team = team,
						Wins = 0,
						Losses = 0,
						Ties = 0,
						Points = 0,
						Rank = 0
					};
					this.repository.Context.Standings.InsertOnSubmit(standing);
				}
			}

			this.repository.SubmitChanges();

		}

		public void UpdateStatistics(int seasonId)
		{
			IEnumerable<Game> games = this.repository.GetGamesWithoutPerformanceSummaries(seasonId);

			foreach (Game game in games)
				this.ProcessGame(game);

			this.repository.SubmitChanges();

			foreach (Player player in this.playersAffected)
			{
				PlayerSeasonPerformance playerPerformance =
					this.repository.GetPlayerSeasonPerformance(player.Id, seasonId);
				if (playerPerformance == null)
				{
					playerPerformance = new PlayerSeasonPerformance();
					this.repository.Context.PlayerSeasonPerformances.InsertOnSubmit(playerPerformance);
				}

				var items = from p in player.PlayerGamePerformances
							where p.SeasonId == seasonId
							select p;

				foreach (PlayerGamePerformance performance in items)
				{
					playerPerformance.Shots += performance.Shots;
					playerPerformance.Goals += performance.Goals;
					playerPerformance.Assists += performance.Assists;
					playerPerformance.PenaltyMinutes += performance.PenaltyMinutes;
					playerPerformance.ShotsAgainst += performance.ShotsAgainst;
					playerPerformance.GoalsAgainst += performance.GoalsAgainst;
				}

				playerPerformance.Points = playerPerformance.Goals + playerPerformance.Assists;
				if (player.Position == Constants.GoalieAbbreviation)
				{
					playerPerformance.GoalsAgainstAverage = (double)playerPerformance.GoalsAgainst / items.Count();
					playerPerformance.SavePercentage = 1.0d - (double)playerPerformance.GoalsAgainst / playerPerformance.ShotsAgainst;
					playerPerformance.ShotPercentage = 0;
				}
				else
				{
					playerPerformance.ShotPercentage = (double)playerPerformance.Goals / playerPerformance.Shots;
					playerPerformance.SavePercentage = 0;
					playerPerformance.GoalsAgainstAverage = 0;
				}
			}

			foreach (Team team in this.teamsAffected)
			{
				TeamSeasonPerformance teamPerformance =
					this.repository.GetTeamSeasonPerformance(team.Id, seasonId);
				if (teamPerformance == null)
				{
					teamPerformance = new TeamSeasonPerformance();
					this.repository.Context.TeamSeasonPerformances.InsertOnSubmit(teamPerformance);
				}

				var items = from p in team.TeamGamePerformances
							where p.SeasonId == seasonId
							select p;

				foreach (TeamGamePerformance performance in items)
				{
					teamPerformance.Shots += performance.Shots;
					teamPerformance.Goals += performance.Goals;
					teamPerformance.Assists += performance.Assists;
					teamPerformance.PenaltyMinutes += performance.PenaltyMinutes;
					teamPerformance.ShotsAgainst += performance.ShotsAgainst;
					teamPerformance.GoalsAgainst += performance.GoalsAgainst;
				}

				if (items.Count() > 0)
					teamPerformance.GoalsAgainstAverage = (double)teamPerformance.GoalsAgainst / items.Count();
				else
					teamPerformance.GoalsAgainstAverage = 0;

				if (teamPerformance.ShotsAgainst > 0)
					teamPerformance.SavePercentage = 1.0d - (double)teamPerformance.GoalsAgainst / teamPerformance.ShotsAgainst;
				else
					teamPerformance.SavePercentage = 1;

				if (teamPerformance.Shots > 0)
					teamPerformance.ShotPercentage = (double)teamPerformance.Goals / teamPerformance.Shots;
				else
					teamPerformance.ShotPercentage = 0;
			}

			this.EnsureStandingsExist(games.First().Season);
			Dictionary<Team, Standing> standings = new Dictionary<Team, Standing>();
			IEnumerable<Standing> seasonStandings = this.repository.GetStandings(seasonId);
			foreach (Standing standing in seasonStandings)
				standings.Add(standing.Team, standing);

			foreach (Team key in standings.Keys)
			{
				Standing standing = standings[key];
				IEnumerable<Game> teamGames = this.repository.GetTeamSchedule(
					seasonId, key.Id);
				foreach (Game game in teamGames)
				{
					if (game.HomeGoals == game.VisitorGoals)
						standing.Ties++;
					else
					{
						if (game.HomeGoals > game.VisitorGoals &
							game.HomeTeamId == key.Id)
							standing.Wins++;
						else
							standing.Losses++;
					}
				}
				standing.Points = standing.Wins * 2 + standing.Ties;
			}

			this.repository.Context.SubmitChanges();
		}

		void ProcessGame(Game game)
		{
			Dictionary<Player, PlayerGamePerformance> playerStats = new Dictionary<Player, PlayerGamePerformance>();
			Dictionary<Team, TeamGamePerformance> teamStats = new Dictionary<Team, TeamGamePerformance>();

			if (!this.teamsAffected.Contains(game.HomeTeam))
				this.teamsAffected.Add(game.HomeTeam);
			if (!this.teamsAffected.Contains(game.VisitingTeam))
				this.teamsAffected.Add(game.VisitingTeam);

			foreach (Player player in game.HomeTeam.Players)
			{
				playerStats.Add(player, new PlayerGamePerformance() { Game = game, Player = player, Season = game.Season });
				if (!this.playersAffected.Contains(player))
					this.playersAffected.Add(player);
			}

			foreach (Player player in game.VisitingTeam.Players)
			{
				playerStats.Add(player, new PlayerGamePerformance() { Game = game, Player = player, Season = game.Season });
				if (!this.playersAffected.Contains(player))
					this.playersAffected.Add(player);
			}

			TeamGamePerformance homeTeamPerformance = new TeamGamePerformance() { Game = game, Team = game.HomeTeam, Season = game.Season };
			TeamGamePerformance visitingTeamPerformance = new TeamGamePerformance() { Game = game, Team = game.VisitingTeam, Season = game.Season };

			teamStats.Add(game.HomeTeam, homeTeamPerformance);
			teamStats.Add(game.VisitingTeam, visitingTeamPerformance);

			foreach (ShotCount shotCount in game.ShotCounts)
			{
				Player goalie = null;
				playerStats[shotCount.Player].Shots = shotCount.Shots;
				teamStats[shotCount.Player.Team].Shots += shotCount.Shots;

				if (shotCount.Player.Team == game.HomeTeam)
				{
					goalie = game.VisitingTeam.GetPlayerByPosition(Constants.GoalieAbbreviation);
					visitingTeamPerformance.ShotsAgainst++;
				}
				else
				{
					goalie = game.HomeTeam.GetPlayerByPosition(Constants.GoalieAbbreviation);
					homeTeamPerformance.ShotsAgainst++;
				}
				playerStats[goalie].ShotsAgainst++;
			}

			foreach (Goal goal in game.Goals)
			{
				playerStats[goal.Shooter].Goals++;
				teamStats[goal.Shooter.Team].Goals++;

				if (goal.Assistor1 != null)
				{
					teamStats[goal.Shooter.Team].Assists++;
					playerStats[goal.Assistor1].Assists++;
				}
				if (goal.Assistor2 != null)
				{
					teamStats[goal.Shooter.Team].Assists++;
					playerStats[goal.Assistor2].Assists++;
				}

				Player goalie = null;
				if (goal.Shooter.Team == game.HomeTeam)
				{
					goalie = game.VisitingTeam.GetPlayerByPosition(Constants.GoalieAbbreviation);
					visitingTeamPerformance.GoalsAgainst++;
				}
				else
				{
					goalie = game.HomeTeam.GetPlayerByPosition(Constants.GoalieAbbreviation);
					homeTeamPerformance.GoalsAgainst++;
				}

				playerStats[goalie].GoalsAgainst++;
			}

			foreach (Penalty penalty in game.Penalties)
			{
				playerStats[penalty.Player].PenaltyMinutes += penalty.Duration;
				teamStats[penalty.Player.Team].PenaltyMinutes += penalty.Duration;
			}

			Player homeGoalie = game.HomeTeam.GetPlayerByPosition(Constants.GoalieAbbreviation);
			Player visitorGoalie = game.HomeTeam.GetPlayerByPosition(Constants.GoalieAbbreviation);

			PlayerGamePerformance homeGoalieStats = playerStats[homeGoalie];
			PlayerGamePerformance visitorGoalieStats = playerStats[visitorGoalie];

			homeGoalieStats.SavePercentage = 1.0d - (double)(homeGoalieStats.GoalsAgainst / homeGoalieStats.ShotsAgainst);
			visitorGoalieStats.SavePercentage = 1.0d - (double)(visitorGoalieStats.GoalsAgainst / visitorGoalieStats.ShotsAgainst);
			homeTeamPerformance.SavePercentage = homeGoalieStats.SavePercentage;
			visitingTeamPerformance.SavePercentage = visitorGoalieStats.SavePercentage;

			foreach (Player key in playerStats.Keys)
				this.repository.Context.PlayerGamePerformances.InsertOnSubmit(playerStats[key]);

			this.repository.Context.TeamGamePerformances.InsertOnSubmit(homeTeamPerformance);
			this.repository.Context.TeamGamePerformances.InsertOnSubmit(visitingTeamPerformance);

		}


	}
}
